package com.pxample.pemo.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import io.netty.channel.ChannelOption;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.http.codec.json.Jackson2JsonDecoder;
import org.springframework.http.codec.json.Jackson2JsonEncoder;
import org.springframework.util.MimeTypeUtils;
import org.springframework.web.reactive.function.client.ExchangeStrategies;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.netty.http.client.HttpClient;
import reactor.netty.resources.ConnectionProvider;

import javax.net.ssl.SSLException;
import java.time.Duration;

@Configuration
public class WebClientConfig {

//    private final LoadBalancerClientFactory loadBalancerClientFactory;

    /*public WebClientConfig(LoadBalancerClientFactory loadBalancerClientFactory) {
        this.loadBalancerClientFactory = loadBalancerClientFactory;
    }*/

    @Bean
    public WebClient.Builder webClientBuilder() {
        return WebClient.builder();
    }

    @Bean
    public WebClient webClient(WebClient.Builder builder) throws SSLException {
//        return builder.build();
        // 注册 JSON 解码器和编码器
        ExchangeStrategies strategies = ExchangeStrategies.builder()
                .codecs(configurer -> {
                    configurer.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(new ObjectMapper(), MediaType.APPLICATION_JSON, MimeTypeUtils.parseMimeType("text/x-json")));
                    configurer.defaultCodecs().jackson2JsonEncoder(new Jackson2JsonEncoder(new ObjectMapper(), MediaType.APPLICATION_JSON, MimeTypeUtils.parseMimeType("text/x-json")));
                })
                .build();

        // SSL验证
        SslContext sslContext = SslContextBuilder
                .forClient()
                .trustManager(InsecureTrustManagerFactory.INSTANCE)
                .build();

        ConnectionProvider provider = ConnectionProvider.builder("custom")
                .maxConnections(200) // 设置连接池中的最大连接数
                .pendingAcquireMaxCount(1000) // 等待队列大小, 默认时400
                .pendingAcquireTimeout(Duration.ofSeconds(20)) // 设置连接等待超时时间。在连接池中等待获取连接的最大时间
//                .maxIdleTime(Duration.ofMinutes(10)) // 设置为空闲连接的最大时间（30秒）。如果连接在这个时间内没有任何活动，它将被关闭
//                .maxLifeTime(Duration.ofMinutes(20)) // 设置为连接的最大生命周期（20分钟）。无论连接是否空闲，当连接存活超过这个时间时，它将被关闭
                .build();

        // 创建 HttpClient 并配置连接池,默认keep-alive开启
        HttpClient httpClient = HttpClient
                .create(provider)
                .compress(true)
                .responseTimeout(Duration.ofSeconds(20)) // 设置响应超时时间。在发出请求后等待响应的最大时间
                .doOnConnected(conn ->
                        // 在进行网络请求时，客户端在等待服务器返回响应数据时的最大等待时间。如果在此时间内未接收到任何数据，客户端会认为读取超时，并抛出相应的异常
                        conn.addHandlerLast(new io.netty.handler.timeout.ReadTimeoutHandler(20))
                                // 它定义了客户端在发送请求数据时的最大等待时间。如果在此时间内未能完成写入操作，客户端将抛出超时异常
                                // 防止写操作阻塞：确保在发送请求时不会因为网络问题或服务器反应迟缓而导致长时间的写入阻塞
                                // 提高系统健壮性：通过限制写入时间，避免由于某个请求长时间无法发送而影响到其他请求的处理
                                .addHandlerLast(new io.netty.handler.timeout.WriteTimeoutHandler(20)) // 写超时
                )
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 20000)// 设置连接超时时间
                .secure(t -> t.sslContext(sslContext));
        // 创建 WebClient 并使用自定义的 HttpClient
        return WebClient
                .builder()
                .clientConnector(new ReactorClientHttpConnector(httpClient))
                .defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE, MediaType.TEXT_PLAIN_VALUE, MediaType.APPLICATION_FORM_URLENCODED_VALUE,"text/x-json")
                .defaultHeader("Accept-Encoding","gzip,deflate")
                .exchangeStrategies(strategies)
                .build();
    }
}
